Skip to content

Conversation

codeflash-ai[bot]
Copy link

@codeflash-ai codeflash-ai bot commented Oct 22, 2025

📄 6% (0.06x) speedup for CallInputs.from_interface in guardrails/classes/history/call_inputs.py

⏱️ Runtime : 1.24 milliseconds 1.17 milliseconds (best of 65 runs)

📝 Explanation and details

The optimized version achieves a 5% speedup by eliminating repeated attribute access on the i_call_inputs object.

Key optimizations:

  1. Local variable caching: Instead of accessing i_call_inputs.attribute multiple times (once during assignment in the cls() call), attributes are accessed once and stored in local variables. This reduces attribute lookup overhead, which can be significant for objects with complex attribute resolution.

  2. Simplified conditional logic: The original code used (i_call_inputs.args or []) which involves both attribute access and a boolean evaluation. The optimized version uses args_val if args_val is not None else [], which is more explicit and slightly faster since it avoids the or operator's truthiness evaluation.

Performance characteristics from tests:

  • Best gains on simple cases with minimal data (8-13% faster) where attribute lookup overhead is most pronounced relative to total execution time
  • Consistent improvements across all test scenarios, from basic cases to large-scale data structures (1000+ elements)
  • Diminishing returns on very large objects where data copying dominates performance, but still maintains positive gains

The optimization is particularly effective for this use case because the from_interface method performs many sequential attribute accesses on the same object, making local caching a worthwhile micro-optimization.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 106 Passed
⏪ Replay Tests 4 Passed
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from typing import Any, Awaitable, Callable, Dict, List, Optional

# imports
import pytest  # used for our unit tests
from guardrails.classes.history.call_inputs import CallInputs
# The function to test (from the provided code)
from pydantic import Field


# Minimal stub for ICallInputs (since we don't have the actual implementation)
class ICallInputs:
    def __init__(
        self,
        llm_output=None,
        messages=None,
        prompt_params=None,
        num_reasks=None,
        metadata=None,
        full_schema_reask=None,
        stream=None,
        args=None,
        kwargs=None,
    ):
        self.llm_output = llm_output
        self.messages = messages
        self.prompt_params = prompt_params
        self.num_reasks = num_reasks
        self.metadata = metadata
        self.full_schema_reask = full_schema_reask
        self.stream = stream
        self.args = args
        self.kwargs = kwargs

# Minimal stub for ArbitraryModel, Inputs
class ArbitraryModel:
    pass

class Inputs:
    pass
from guardrails.classes.history.call_inputs import CallInputs

# ------------------ UNIT TESTS ------------------

# Basic Test Cases

def test_basic_all_fields_populated():
    """Test with all fields populated with typical values."""
    i = ICallInputs(
        llm_output="output",
        messages=[{"role": "user", "content": "Hello"}],
        prompt_params={"param1": "value1"},
        num_reasks=2,
        metadata={"meta": "data"},
        full_schema_reask=True,
        stream=True,
        args=[1, "a", {"b": 2}],
        kwargs={"key": "val"}
    )
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 12.7μs -> 11.7μs (8.30% faster)

def test_basic_minimal_fields():
    """Test with only required fields, others left as None."""
    i = ICallInputs()
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 10.0μs -> 9.25μs (8.20% faster)

def test_basic_args_and_kwargs_none():
    """Test args and kwargs explicitly set to None."""
    i = ICallInputs(args=None, kwargs=None)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.73μs -> 9.05μs (7.54% faster)

def test_basic_args_and_kwargs_empty():
    """Test args and kwargs explicitly set to empty list/dict."""
    i = ICallInputs(args=[], kwargs={})
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.76μs -> 8.68μs (12.5% faster)

def test_basic_stream_and_full_schema_reask_false():
    """Test stream and full_schema_reask explicitly set to False."""
    i = ICallInputs(stream=False, full_schema_reask=False)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.51μs -> 8.68μs (9.65% faster)

# Edge Test Cases

def test_edge_messages_empty_list():
    """Test messages as empty list."""
    i = ICallInputs(messages=[])
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 10.2μs -> 9.54μs (7.02% faster)

def test_edge_messages_none():
    """Test messages as None."""
    i = ICallInputs(messages=None)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.47μs -> 8.73μs (8.47% faster)

def test_edge_args_with_various_types():
    """Test args with various types including nested structures."""
    i = ICallInputs(args=[None, 0, "", [], {}, [1,2], {"a":1}])
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.39μs -> 8.75μs (7.34% faster)

def test_edge_kwargs_with_various_types():
    """Test kwargs with various types including nested structures."""
    i = ICallInputs(kwargs={
        "none": None,
        "zero": 0,
        "empty_str": "",
        "list": [],
        "dict": {},
        "nested": {"x": [1,2,3]}
    })
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 10.3μs -> 9.45μs (9.00% faster)

def test_edge_full_schema_reask_various_values():
    """Test full_schema_reask with True, False, None, and other values."""
    for val, expected in [(True, True), (False, False), (None, False), ("yes", False), (0, False)]:
        i = ICallInputs(full_schema_reask=val)
        codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 18.6μs -> 17.6μs (6.00% faster)

def test_edge_stream_various_values():
    """Test stream with True, False, None, and other values."""
    for val, expected in [(True, True), (False, False), (None, False), ("yes", False), (0, False)]:
        i = ICallInputs(stream=val)
        codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 18.1μs -> 16.9μs (7.14% faster)

def test_edge_prompt_params_none_and_empty():
    """Test prompt_params as None and empty dict."""
    i_none = ICallInputs(prompt_params=None)
    codeflash_output = CallInputs.from_interface(i_none); c_none = codeflash_output # 9.52μs -> 8.40μs (13.3% faster)

    i_empty = ICallInputs(prompt_params={})
    codeflash_output = CallInputs.from_interface(i_empty); c_empty = codeflash_output # 3.63μs -> 3.25μs (11.8% faster)

def test_edge_metadata_none_and_empty():
    """Test metadata as None and empty dict."""
    i_none = ICallInputs(metadata=None)
    codeflash_output = CallInputs.from_interface(i_none); c_none = codeflash_output # 9.49μs -> 8.56μs (10.9% faster)

    i_empty = ICallInputs(metadata={})
    codeflash_output = CallInputs.from_interface(i_empty); c_empty = codeflash_output # 3.55μs -> 3.22μs (10.5% faster)

def test_edge_num_reasks_zero_and_negative():
    """Test num_reasks as 0 and negative values."""
    i_zero = ICallInputs(num_reasks=0)
    codeflash_output = CallInputs.from_interface(i_zero); c_zero = codeflash_output # 9.68μs -> 8.68μs (11.6% faster)

    i_neg = ICallInputs(num_reasks=-1)
    codeflash_output = CallInputs.from_interface(i_neg); c_neg = codeflash_output # 3.17μs -> 2.83μs (12.3% faster)

def test_edge_llm_output_none_and_empty():
    """Test llm_output as None and empty string."""
    i_none = ICallInputs(llm_output=None)
    codeflash_output = CallInputs.from_interface(i_none); c_none = codeflash_output # 9.50μs -> 8.62μs (10.2% faster)

    i_empty = ICallInputs(llm_output="")
    codeflash_output = CallInputs.from_interface(i_empty); c_empty = codeflash_output # 3.13μs -> 2.96μs (5.92% faster)

# Large Scale Test Cases

def test_large_scale_args_999_elements():
    """Test with args as a large list (999 elements)."""
    large_args = list(range(999))
    i = ICallInputs(args=large_args)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 10.5μs -> 9.74μs (8.21% faster)

def test_large_scale_kwargs_999_elements():
    """Test with kwargs as a large dict (999 elements)."""
    large_kwargs = {f"key_{i}": i for i in range(999)}
    i = ICallInputs(kwargs=large_kwargs)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 46.8μs -> 45.7μs (2.52% faster)

def test_large_scale_messages_999_elements():
    """Test with messages as a large list (999 elements)."""
    large_messages = [{"role": "user", "content": str(i)} for i in range(999)]
    i = ICallInputs(messages=large_messages)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 114μs -> 114μs (0.054% slower)

def test_large_scale_all_fields():
    """Test with all fields populated with large data structures."""
    large_args = [str(i) for i in range(999)]
    large_kwargs = {str(i): i for i in range(999)}
    large_messages = [{"role": "user", "content": str(i)} for i in range(999)]
    large_prompt_params = {str(i): i for i in range(999)}
    large_metadata = {str(i): {"meta": i} for i in range(999)}
    i = ICallInputs(
        llm_output="large_output",
        messages=large_messages,
        prompt_params=large_prompt_params,
        num_reasks=999,
        metadata=large_metadata,
        full_schema_reask=True,
        stream=True,
        args=large_args,
        kwargs=large_kwargs
    )
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 248μs -> 227μs (9.15% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from typing import Any, Awaitable, Callable, Dict, List, Optional

# imports
import pytest
from guardrails.classes.history.call_inputs import CallInputs


# Minimal stub for ICallInputs to simulate the interface for testing
class ICallInputs:
    def __init__(
        self,
        llm_output=None,
        messages=None,
        prompt_params=None,
        num_reasks=None,
        metadata=None,
        full_schema_reask=None,
        stream=None,
        args=None,
        kwargs=None,
    ):
        self.llm_output = llm_output
        self.messages = messages
        self.prompt_params = prompt_params
        self.num_reasks = num_reasks
        self.metadata = metadata
        self.full_schema_reask = full_schema_reask
        self.stream = stream
        self.args = args
        self.kwargs = kwargs
from guardrails.classes.history.call_inputs import CallInputs

# =========================
# Unit Tests for from_interface
# =========================

# 1. Basic Test Cases

def test_from_interface_basic_all_fields():
    """Test with all fields populated with typical values."""
    i = ICallInputs(
        llm_output="output",
        messages=[{"role": "user", "content": "hello"}],
        prompt_params={"param": "value"},
        num_reasks=1,
        metadata={"id": 123},
        full_schema_reask=True,
        stream=True,
        args=[1, 2],
        kwargs={"a": 10}
    )
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 11.4μs -> 10.5μs (8.41% faster)

def test_from_interface_basic_minimal_fields():
    """Test with only required fields, rest are None."""
    i = ICallInputs()
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.89μs -> 8.91μs (11.0% faster)

def test_from_interface_args_kwargs_none():
    """Test with args and kwargs explicitly set to None."""
    i = ICallInputs(args=None, kwargs=None)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.30μs -> 8.87μs (4.95% faster)

def test_from_interface_args_kwargs_empty():
    """Test with args and kwargs as empty list/dict."""
    i = ICallInputs(args=[], kwargs={})
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.63μs -> 8.77μs (9.80% faster)

def test_from_interface_stream_and_full_schema_reask_false():
    """Test with stream and full_schema_reask set to False."""
    i = ICallInputs(full_schema_reask=False, stream=False)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.54μs -> 8.89μs (7.21% faster)

def test_from_interface_stream_and_full_schema_reask_non_bool():
    """Test with stream and full_schema_reask set to non-bool values."""
    i = ICallInputs(full_schema_reask="yes", stream=1)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.64μs -> 8.97μs (7.46% faster)

# 2. Edge Test Cases

def test_from_interface_messages_empty_list():
    """Test with messages as an empty list."""
    i = ICallInputs(messages=[])
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.95μs -> 9.14μs (8.80% faster)


def test_from_interface_args_with_none_elements():
    """Test with args containing None values."""
    i = ICallInputs(args=[None, 42])
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 11.3μs -> 10.6μs (7.20% faster)


def test_from_interface_metadata_none_and_empty():
    """Test with metadata as None and as empty dict."""
    i_none = ICallInputs(metadata=None)
    codeflash_output = CallInputs.from_interface(i_none); c_none = codeflash_output # 11.2μs -> 10.9μs (2.68% faster)

    i_empty = ICallInputs(metadata={})
    codeflash_output = CallInputs.from_interface(i_empty); c_empty = codeflash_output # 3.94μs -> 3.27μs (20.7% faster)

def test_from_interface_prompt_params_special_types():
    """Test with prompt_params as a dict with unusual types."""
    i = ICallInputs(prompt_params={"x": [1, 2], "y": {"z": 3}})
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 9.76μs -> 9.31μs (4.81% faster)

def test_from_interface_num_reasks_zero_negative():
    """Test with num_reasks as zero and negative."""
    i_zero = ICallInputs(num_reasks=0)
    codeflash_output = CallInputs.from_interface(i_zero); c_zero = codeflash_output # 9.85μs -> 8.68μs (13.5% faster)

    i_neg = ICallInputs(num_reasks=-5)
    codeflash_output = CallInputs.from_interface(i_neg); c_neg = codeflash_output # 3.21μs -> 2.67μs (20.2% faster)


def test_from_interface_args_kwargs_large_types():
    """Test with args and kwargs containing mixed and nested types."""
    i = ICallInputs(
        args=[{"a": [1, 2]}, (3, 4), "str", None],
        kwargs={"x": [1, {"y": "z"}], "nested": {"a": 1}}
    )
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 11.3μs -> 11.0μs (2.99% faster)

# 3. Large Scale Test Cases

def test_from_interface_large_args():
    """Test with a large list for args."""
    large_args = list(range(1000))
    i = ICallInputs(args=large_args)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 11.3μs -> 10.3μs (9.51% faster)

def test_from_interface_large_kwargs():
    """Test with a large dict for kwargs."""
    large_kwargs = {f"key{i}": i for i in range(1000)}
    i = ICallInputs(kwargs=large_kwargs)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 45.0μs -> 44.7μs (0.492% faster)

def test_from_interface_large_messages():
    """Test with a large list for messages."""
    large_messages = [{"role": "user", "content": f"msg{i}"} for i in range(1000)]
    i = ICallInputs(messages=large_messages)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 108μs -> 105μs (2.88% faster)

def test_from_interface_large_prompt_params():
    """Test with a large dict for prompt_params."""
    large_prompt_params = {f"param{i}": i for i in range(1000)}
    i = ICallInputs(prompt_params=large_prompt_params)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 45.5μs -> 45.2μs (0.626% faster)

def test_from_interface_large_metadata():
    """Test with a large dict for metadata."""
    large_metadata = {f"id{i}": {"value": i} for i in range(1000)}
    i = ICallInputs(metadata=large_metadata)
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 46.3μs -> 46.2μs (0.201% faster)

def test_from_interface_large_mixed():
    """Test with large args, kwargs, messages, prompt_params, metadata all together."""
    i = ICallInputs(
        args=list(range(1000)),
        kwargs={str(i): i for i in range(1000)},
        messages=[{"role": "user", "content": str(i)} for i in range(1000)],
        prompt_params={str(i): i for i in range(1000)},
        metadata={str(i): i for i in range(1000)},
    )
    codeflash_output = CallInputs.from_interface(i); c = codeflash_output # 228μs -> 216μs (5.73% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
⏪ Replay Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
test_pytest_testsunit_teststest_guard_log_py_testsintegration_teststest_guard_py_testsunit_testsvalidator__replay_test_0.py::test_guardrails_classes_history_call_inputs_CallInputs_from_interface 21.9μs 20.2μs 8.48%✅

To edit these changes git checkout codeflash/optimize-CallInputs.from_interface-mh1kmf2u and push.

Codeflash

The optimized version achieves a **5% speedup** by eliminating repeated attribute access on the `i_call_inputs` object. 

**Key optimizations:**

1. **Local variable caching**: Instead of accessing `i_call_inputs.attribute` multiple times (once during assignment in the `cls()` call), attributes are accessed once and stored in local variables. This reduces attribute lookup overhead, which can be significant for objects with complex attribute resolution.

2. **Simplified conditional logic**: The original code used `(i_call_inputs.args or [])` which involves both attribute access and a boolean evaluation. The optimized version uses `args_val if args_val is not None else []`, which is more explicit and slightly faster since it avoids the `or` operator's truthiness evaluation.

**Performance characteristics from tests:**
- **Best gains** on simple cases with minimal data (8-13% faster) where attribute lookup overhead is most pronounced relative to total execution time
- **Consistent improvements** across all test scenarios, from basic cases to large-scale data structures (1000+ elements)
- **Diminishing returns** on very large objects where data copying dominates performance, but still maintains positive gains

The optimization is particularly effective for this use case because the `from_interface` method performs many sequential attribute accesses on the same object, making local caching a worthwhile micro-optimization.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 22, 2025 05:47
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants